﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Coding4Fun.Kinect.Wpf;
using Microsoft.Research.Kinect.Nui;
namespace _3DSensor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        //Kinect Runtime
         Runtime _nui = new Runtime();
         GestureRecogniser _recogniser = new GestureRecogniser();
        X10GestureApp _x10App;
        PaintGestureApp _paintApp;
        bool _isAppSelected;

        public MainWindow()
        {
            InitializeComponent();
        }




        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            //Initialize to do skeletal tracking
            _nui.Initialize(RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseColor);
            
            #region TransformSmooth
            //Must set to true and set after call to Initialize
            _nui.SkeletonEngine.TransformSmooth = true;

            //Use to transform and reduce jitter
            var parameters = new TransformSmoothParameters
            {
                Smoothing = 0.90f,
                Correction = 0f,
                Prediction = 0f,
                JitterRadius = 0.01f,
                MaxDeviationRadius = 0.04f
            };

            _nui.SkeletonEngine.SmoothParameters = parameters;

            #endregion

            //add event to receive skeleton data
            _nui.SkeletonFrameReady += NuiSkeletonFrameReady;
            _nui.VideoFrameReady += NuiVideoFrameReady;
            _nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution1280x1024, ImageType.Color);
        }

        void NuiVideoFrameReady(object sender, ImageFrameReadyEventArgs e)
        {
            bg.Source = e.ImageFrame.ToBitmapSource();
            if (_x10App != null && _paintApp != null && !_x10App.IsAppActive && !_paintApp.IsAppActive)
                            lblGesture.Content = "Place hand on an app to select light control or paint";
            //create the associated app
            if (_x10App == null)
            {
                _x10App = new X10GestureApp(_recogniser) {Title = "X10"};
            }
            if (_paintApp == null)
            {
                _paintApp = new PaintGestureApp(_recogniser) {Title = "Paint App"};
            }

            

            if (_x10App == null || !_x10App.IsAppActive) return;
            lblLamp.Content = _x10App.CurrentBrightness + "%";
            sliderBrightness.Value = _x10App.CurrentBrightness;
            targetSlider.Value = _x10App.NextTargetBrightness;
            lblTarget.Content = _x10App.NextTargetBrightness + "%";

            lblGesture.Content = !sliderBrightness.Value.Equals(targetSlider.Value) ? "Busy!" : "ree!";
        }

        void NuiSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {  
            var allSkeletons = e.SkeletonFrame;
            //get the first tracked skeleton
            var skeleton = (from s in allSkeletons.Skeletons
                                     where s.TrackingState == SkeletonTrackingState.Tracked
                                     select s).FirstOrDefault();
            if (skeleton == null) return;
            //set position
            SetEllipsePosition(headEllipse, skeleton.Joints[JointID.Head]);
            SetEllipsePosition(leftEllipse, skeleton.Joints[JointID.HandLeft]);
            SetEllipsePosition(rightEllipse, skeleton.Joints[JointID.HandRight]);
            SetEllipsePosition(rightElbowEllipse, skeleton.Joints[JointID.ElbowRight]);
            SetEllipsePosition(rightShoulderEllipse, skeleton.Joints[JointID.ShoulderRight]);
            //join the ellipses
            ////draw head->lefthand
            lineHeadLHand.X1 = Canvas.GetLeft(headEllipse) + headEllipse.Width/2;
            lineHeadLHand.Y1 = Canvas.GetTop(headEllipse) + headEllipse.Width / 2;
            lineHeadLHand.X2 = Canvas.GetLeft(leftEllipse) + headEllipse.Width / 2;
            lineHeadLHand.Y2 = Canvas.GetTop(leftEllipse) + headEllipse.Width / 2;
           
            ////draw head->rightSholder
            lineHeadSholder.X1 = Canvas.GetLeft(headEllipse) + headEllipse.Width / 2;
            lineHeadSholder.Y1 = Canvas.GetTop(headEllipse) + headEllipse.Width / 2;
            lineHeadSholder.X2 = Canvas.GetLeft(rightShoulderEllipse) + headEllipse.Width / 2;
            lineHeadSholder.Y2 = Canvas.GetTop(rightShoulderEllipse) + headEllipse.Width / 2;
            ////draw RightSholder->rightElbow
            lineSholderRElbow.X1 = Canvas.GetLeft(rightShoulderEllipse) + headEllipse.Width / 2;
            lineSholderRElbow.Y1 = Canvas.GetTop(rightShoulderEllipse) + headEllipse.Width / 2;
            lineSholderRElbow.X2 = Canvas.GetLeft(rightElbowEllipse) + headEllipse.Width / 2;
            lineSholderRElbow.Y2 = Canvas.GetTop(rightElbowEllipse) + headEllipse.Width / 2;
            ////draw rightElbow->righthand
            lineRElbowRHand.X1 = Canvas.GetLeft(rightElbowEllipse) + headEllipse.Width / 2;
            lineRElbowRHand.Y1 = Canvas.GetTop(rightElbowEllipse) + headEllipse.Width / 2;
            lineRElbowRHand.X2 = Canvas.GetLeft(rightEllipse) + headEllipse.Width / 2;
            lineRElbowRHand.Y2 = Canvas.GetTop(rightEllipse) + headEllipse.Width / 2;
            
            
            ProcessSkeleton(skeleton);
        }


        private void CheckAppSelected()
        {
            var leftHandEllipseTop = Canvas.GetTop(leftEllipse);
            var rightHandEllipseTop = Canvas.GetTop(rightEllipse);
            var leftHandellipseLeft = Canvas.GetLeft(leftEllipse);
            var rightHandEllipseRight = Canvas.GetLeft(rightEllipse);

            var selectedBrush = new SolidColorBrush(Colors.Yellow);
            var x10OriginalBrush = new SolidColorBrush(Colors.Red);
            var paintOriginalBrush = new SolidColorBrush(Colors.Green);
            

            if (((leftHandEllipseTop >= Canvas.GetTop(rectX10) && leftHandEllipseTop <= (Canvas.GetTop(rectX10) + rectX10.Height)) &&
                (leftHandellipseLeft >= Canvas.GetLeft(rectX10) && leftHandellipseLeft <= (Canvas.GetLeft(rectX10) + rectX10.Width))) ||
                ((rightHandEllipseTop >= Canvas.GetTop(rectX10) && rightHandEllipseTop <= (Canvas.GetTop(rectX10) + rectX10.Height)) &&
                (rightHandEllipseRight >= Canvas.GetLeft(rectX10) && rightHandEllipseRight <= (Canvas.GetLeft(rectX10) + rectX10.Width))))
            {
                    
                rectX10.Fill = selectedBrush;
                _x10App.IsAppActive = true;

                rectPaint.Fill = paintOriginalBrush;                    
                _paintApp.IsAppActive = false;

                _isAppSelected = true;
                lblApp.Content = "X10: Wait for the X10 slider to catch with Kinect slider before issuing new commands";
                x10Instr.Visibility = Visibility.Visible;
                return;
            }
  

            x10Instr.Visibility = Visibility.Hidden;
           if (((leftHandEllipseTop >= Canvas.GetTop(rectPaint) && leftHandEllipseTop <= (Canvas.GetTop(rectPaint) + rectPaint.Height)) &&
                    (leftHandellipseLeft >= Canvas.GetLeft(rectPaint) && leftHandellipseLeft <= (Canvas.GetLeft(rectPaint) + rectPaint.Width))) ||
                    ((rightHandEllipseTop >= Canvas.GetTop(rectPaint) && rightHandEllipseTop <= (Canvas.GetTop(rectPaint) + rectPaint.Height)) &&
                    (rightHandEllipseRight >= Canvas.GetLeft(rectPaint) && rightHandEllipseRight <= (Canvas.GetLeft(rectPaint) + rectPaint.Width))))
            {
               
                    rectX10.Fill = x10OriginalBrush;
                    _x10App.IsAppActive = false;

                    rectPaint.Fill = selectedBrush;
                    _paintApp.IsAppActive = true;
                    x10Instr.Visibility = Visibility.Hidden;
                    
                    _isAppSelected = true;
                    lblApp.Content = "Paint";
                    return;
            }            
            

            if (_paintApp != null && !_paintApp.IsAppActive)
                rectPaint.Fill = paintOriginalBrush;

            if (_x10App != null && !_x10App.IsActive)
                rectX10.Fill = x10OriginalBrush;

            return;
        }

        int _prevPercent;
        
        private void ProcessSkeleton(SkeletonData skeleton)
        {
            var headY = skeleton.Joints[JointID.Head].Position.Y;
            var headX = skeleton.Joints[JointID.Head].Position.X;
            var rHandY = skeleton.Joints[JointID.HandRight].Position.Y;
            var rHandX = skeleton.Joints[JointID.HandRight].Position.X;
            var lHandY = skeleton.Joints[JointID.HandLeft].Position.Y;
            var lHandX = skeleton.Joints[JointID.HandLeft].Position.X;
            var rElbowY = skeleton.Joints[JointID.ElbowRight].Position.Y;
            var rShoulderX = skeleton.Joints[JointID.ShoulderRight].Position.X;
            var lShoulderX = skeleton.Joints[JointID.ShoulderLeft].Position.X;
            
            if (!_isAppSelected)
            {
                CheckAppSelected();
            }
            else
            {
                
                var sendGesture = new GestureBase();
                if (rHandY > headY)
                {
                    sendGesture.InnerGesture = Gesture.On;
                    _recogniser.PublishGesture(sendGesture);
                    lblGesture.Content = "ON";
                }
                else if (lHandY > headY)
                {
                    sendGesture.InnerGesture = Gesture.Off; 
                    _recogniser.PublishGesture(sendGesture);
                    lblGesture.Content = "Off";
                }
                else if(rHandX<headX && lHandX>headX)
                {
                    sendGesture.InnerGesture = Gesture.Close; 
                    _recogniser.PublishGesture(sendGesture);
                    lblGesture.Content = "Close";
                    lblApp.Content = "";
                    _isAppSelected = false;
                }
                else if (rHandY < headY && rHandY > rElbowY && rHandX >= headX)
                {
                    double span = Math.Abs(headX - rShoulderX)*3;
                    double deviation = rHandX - headX;
                    if (deviation > span) deviation = span;
                    var percent = Convert.ToInt32((deviation*100.0)/(span));
                
                    sendGesture.InnerGesture = percent > _prevPercent ? Gesture.Increase : Gesture.Decrease;
                    sendGesture.Value = percent;
                    _prevPercent = percent;
                    lblGesture.Content = sendGesture.InnerGesture.ToString();
                    _recogniser.PublishGesture(sendGesture);

                }
                if (headX-lHandX > 2*(headX-lShoulderX) )
                {
                    sendGesture.InnerGesture = Gesture.LeftHandOut;
                    _recogniser.PublishGesture(sendGesture);
                }

                sendGesture.InnerGesture = Gesture.Move;

                //Scale to 640x480.  Re-scale later if necessary
                var scaledPosition = skeleton.Joints[JointID.HandRight].ScaleTo(640, 480).Position;
                sendGesture.Value = scaledPosition.X;
                sendGesture.Value2 = scaledPosition.Y;
                _recogniser.PublishGesture(sendGesture);

                
            }
        }

        
        private static void SetEllipsePosition(FrameworkElement ellipse, Joint joint)
        {
            var scaledJoint = joint.ScaleTo(640, 480, 0.5f, 0.5f);
            Canvas.SetLeft(ellipse, scaledJoint.Position.X- 25);
            Canvas.SetTop(ellipse, scaledJoint.Position.Y- 25);
        }

        private void WindowClosed(object sender, EventArgs e)
        {   
            _x10App.Close();
            _paintApp.Close();
            Util.killProcess("X10nets.exe");        
            Application.Current.Shutdown(0);           
        }
        
        private void WindowKeyDown(object sender, KeyEventArgs e)
        {
            try
            {
                if (e.Key.Equals(Key.Up) && _nui.NuiCamera.ElevationAngle < 22)
                    _nui.NuiCamera.ElevationAngle += 5;
                else if (e.Key.Equals(Key.Down) && _nui.NuiCamera.ElevationAngle > 10)
                    _nui.NuiCamera.ElevationAngle -= 5;
                base.OnKeyDown(e);
            }
            catch (Exception)
            {
               
            }
        }
    }
    public static partial class ControlExtension
    {
        public static void UpdateControlSafe(this Control control, Action action)
        {
            if (control.Dispatcher.CheckAccess())
            { }
        }
    }
}
